package moodish.server;

import java.util.LinkedList;

import moodish.comm.ServerComm;
import moodish.comm.ServerSideMessage;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.border.Border;
import javax.swing.table.DefaultTableModel;

/**
 * <p>
 * Trata-se do servidor do Moodish.
 * </p>
 * <p>
 * O servidor possu� as seguintes fun��es:
 * </p>
 * <ul>
 * <li>apresenta informa��o sobre quem � amigo de quem e quem deixou de o ser.
 * <li>envia e recebe mensagens dos utilizadores.
 * <li>apresenta um log de actividades dos utilizadores, com registo da
 * respectiva actividade e o tempo em que ocorreu a mesma.
 * <li>apresenta e actualiza a lista de utilizadores online, conforme se muda de
 * Mood ou se algum utilizador se desconectar/conectar.
 * </ul>
 *
 * @author Rodrigo Correia
 * @author Liliana Rocha
 * @author Antonio Raimundo
 */
public class MoodishServerDummy implements MoodishServer {

    private final String[] COLUNAS_USER_TABLE = {"Username", "Mood"};
    private final String[] COLUNAS_LOG_TABLE = {"Time", "Event"};

    private LinkedList<User> users_online = new LinkedList<User>();
    private Object[][] log = new Object[0][2];
    private DefaultTableModel user_model = new DefaultTableModel(updateLista_Online(), COLUNAS_USER_TABLE);
    private JTable user_table = new JTable(user_model);
    private DefaultTableModel log_model = new DefaultTableModel(log, COLUNAS_LOG_TABLE);
    private JTable log_table = new JTable(log_model);

    private ServerGUI janela = null;

    @Override
    public void start(ServerComm serverComm) {

        serverComm.start();
        janela = new ServerGUI();

        while (true) {
            ServerSideMessage message = serverComm.getNextMessage();

            if (message != null) {
                if (message.getType() == ServerSideMessage.Type.CLIENT_CONNECTED) {
                    boolean nickname_in_use = false;
                    for (User u : users_online) {
                        if (u.getNickname().equals(message.getClientNickname())) {
                            nickname_in_use = true;
                        }
                    }

                    if (!nickname_in_use) {
                        for (User u : users_online) {
                            serverComm.sendClientConnected(u.getNickname(),
                                    message.getClientNickname());
                            serverComm.sendClientConnected(message.getClientNickname(), u.getNickname());
                        }

                        clientOn(message.getClientNickname());
                        actualizar_Lista_Users();
                        updateLog(getTime(), message.getClientNickname()
                                + " is now Online!");
                    }else{
                        serverComm.sendClientDisconnected(message.getClientNickname(), message.getClientNickname());
                    }
                } else if (message.getType() == ServerSideMessage.Type.CLIENT_DISCONNECTED) {
                    clientOff(message.getClientNickname());
                    actualizar_Lista_Users();
                    updateLog(getTime(), message.getClientNickname() + " is now Offline!");
                    for (User u : users_online) {
                        for(User friend: u.getFriendsOf()){
                            if(friend.getNickname().equals(message.getClientNickname())){
                                u.getFriendsOf().remove(friend);
                            }
                        }
                        serverComm.sendClientDisconnected(u.getNickname(),
                                message.getClientNickname());
                    }
                } else if (message.getType() == ServerSideMessage.Type.FRIENDSHIP) {
                    User user1 = getUser(message.getClientNickname());
                    User user2 = getUser(message.getPayload());
                    if (user1 == null || user2 == null) {
                        updateLog(getTime(), "Error! '" + message.getClientNickname() + "' or '" + message.getPayload() + "' is not a user!");
                    } else {
                        addFriends(user1, user2);
                        serverComm.sendNewFriendship(user2.getNickname(),
                                user1.getNickname());
                        updateLog(getTime(), user1.getNickname()
                                + " is now friend of " + user2.getNickname() + "!");
                    }
                } else if (message.getType() == ServerSideMessage.Type.UNFREINDSHIP) {
                    User user1 = getUser(message.getClientNickname());
                    User user2 = getUser(message.getPayload());
                    if (user1 == null || user2 == null) {
                        updateLog(getTime(), "Error! '" + message.getClientNickname() + "' or '" + message.getPayload() + "' is not a user!");
                    } else {
                        rmvFriends(user1, user2);
                        serverComm.sendNewUnfriendship(user2.getNickname(),
                                user1.getNickname());
                        updateLog(getTime(), user1.getNickname() + " don't like "
                                + user2.getNickname()
                                + " and break the friendship!");
                    }
                } else if (message.getType() == ServerSideMessage.Type.MOODISH_MESSAGE) {
                    User user = getUser(message.getClientNickname());
                    user.setMood(message.getPayload());
                    actualizar_Lista_Users();
                    updateLog(getTime(),
                            user.getNickname() + " is " + message.getPayload()
                            + "!");
                    for (User u : user.getFriendsOf()) {
                        serverComm.sendMoodishMessage(user.getNickname(),
                                u.getNickname(), "is " + message.getPayload());
                    }
                }
                janela.revalidate();
                janela.repaint();
            }
        }
    }

    /**
     * <p>
     * Actualiza a lista de utilizadores online, com o seu respectivo mood
     * tambem actualizado.
     * </p>
     */
    private void actualizar_Lista_Users() {
        Object[][] users = updateLista_Online();
        int r = user_model.getRowCount();
        for (int u = 0; u < r; u++) {
            user_model.removeRow(0);
        }

        for (int u = 0; u < users.length; u++) {
            user_model.addRow(users[u]);
        }

        user_table.repaint();
        user_model.fireTableDataChanged();
    }

    /**
     * <p>
     * Actualiza a lista de eventos do servidor (log), que vai alterando caso
     * ocorra algum destes acontecimentos:
     * </p>
     * <ul>
     * <li>Um novo utilizador est� Online.
     * <li>Um certo utilizador desconectou-se e encontra-se agora Offline.
     * <li>O utilizador 'x' adicionou o utilizador 'y' como amigo.
     * <li>O utilizador 'x' removeu o utilizador 'y' como amigo.
     * <li>Quando existe alguma altera��o de Mood.
     * </ul>
     * </p>
     */
    private void update_Lista_Log() {

        log_model.addRow(log[log.length - 1]);

        log_table.repaint();
        log_model.fireTableDataChanged();
    }

    /**
     * <p>
     * Constr�i toda a GUI do servidor, para uma intera��o mais 'amig�vel' para
     * quem usar o servidor do Moodish.</p>
     *
     * <p>
     * A interface cont�m toda a informa��o necess�ria para consulta, de maneira
     * visual, atrav�s de {@link LinkedList} de Utilizadores embutidas em 2
     * {@link JTable}. Uma para apresentar os utilizadores online e o seu Mood
     * actual, e outra para apresentar o log do servidor.</p>
     *
     * @author Rodrigo Correia
     * @author Liliana Rocha
     * @author Antonio Raimundo
     */
    private class ServerGUI extends JFrame {

        private static final long serialVersionUID = 1L;
        private Container container;
        private final int X_SIZE = 800, Y_SIZE = 350;

        public ServerGUI() {
            container = super.getContentPane();
            container.setLayout(new BorderLayout());

            container.add(midPanel(), BorderLayout.CENTER);
            container.add(botPanel(), BorderLayout.PAGE_END);

            super.setTitle("Moodish Server @ by Group 14");
            super.setVisible(true);
            super.setLocation(275, 75);
            super.setSize(800, 600);
            super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }

        /**
         * <p>
         * Representa o painel central.</p>
         *
         * @return panel
         */
        private JPanel midPanel() {
            JPanel panel = new JPanel();
            panel.setLayout(new BorderLayout());

            Border border = BorderFactory.createTitledBorder("Online users");
            panel.setBorder(border);

            user_table.setFillsViewportHeight(true);
            user_table.setShowGrid(false);
            user_table.getTableHeader().setReorderingAllowed(false);

            JScrollPane scroll = new JScrollPane(user_table);
            panel.add(scroll);
            return panel;
        }

        /**
         * <p>
         * Representa o painel de fundo.</p>
         *
         * @return panel
         */
        private JPanel botPanel() {
            JPanel panel = new JPanel();
            panel.setLayout(new BorderLayout());
            panel.setPreferredSize(new Dimension(X_SIZE, Y_SIZE));

            Border border = BorderFactory.createTitledBorder("Server log");
            panel.setBorder(border);

            log_table.setShowGrid(false);
            log_table.getTableHeader().setReorderingAllowed(false);
            log_table.getColumnModel().getColumn(0).setMaxWidth(100);
            log_table.getColumnModel().getColumn(0).setResizable(false);
            log_table.setFillsViewportHeight(true);

            JScrollPane scroll = new JScrollPane(log_table);
            panel.add(scroll);

            return panel;
        }
    }

    /**
     * <p>
     * Adiciona o utilizador 'nickname' � lista de Utilizadores Online.</p>
     *
     * @param nickname do utilizador.
     */
    private void clientOn(String nickname) {
        users_online.add(new User(nickname));
    }

    /**
     * <p>
     * Remove o utilizador 'nickname' da lista de Utilizadores Online.</p>
     *
     * @param nickname do utilizador.
     */
    private void clientOff(String nickname) {
        User user = null;
        for (User u : users_online) {
            if (u.getNickname().equals(nickname)) {
                user = u;
            }
        }
        if(user!=null){
            users_online.remove(user);
        }
    }

    /**
     * <p>
     * Retorna o utilizador com o determinado 'nickname'.</p>
     *
     * @param nickname do Utilizador.
     * @return user - O utilizador.
     */
    private User getUser(String nickname) {
        User user = null;

        for (User u : users_online) {
            if (u.getNickname().equals(nickname)) {
                user = u;
            }
        }

        return user;
    }

    /**
     * <p>
     * Adiciona o 'user2' � lista de amigos do 'user1'.</p>
     *
     * @param user1
     * @param user2
     */
    private void addFriends(User user1, User user2) {
        user1.addFriend(user2);
        user2.addFriendOf(user1);
    }

    /**
     * <p>
     * Remove o 'user2' da lista de amigos do 'user1'.</p>
     *
     * @param user1
     * @param user2
     */
    private void rmvFriends(User user1, User user2) {
        user1.rmvFriend(user2);
    }

    /**
     * <p>
     * Retorna a lista de utilizadores online.</p>
     *
     * @return users_online - Representa a tablea dos utilizadores online.
     */
    public LinkedList<User> getUsers_online() {
        return users_online;
    }

    /**
     * <p>
     * Actualiza a lista de utilizadores online, actualizando tamb�m o seu
     * Mood.</p>
     *
     * @return dados - Representam os dados da tabela de utilizadores online.
     */
    private Object[][] updateLista_Online() {
        Object[][] dados;
        int x = users_online.size();
        if (x == 0) {
            dados = new Object[0][2];
        } else {
            dados = new Object[x][2];
        }

        for (int i = 0; i < users_online.size(); i++) {
            User u = users_online.get(i);

            dados[i][0] = u.getNickname();
            dados[i][1] = u.getMood();
        }
        return dados;
    }

    /**
     * <p>
     * Actualiza o log do servidor, com o nome dos �ltimos acontecimentos e a
     * hora em que os mesmos ocorreram.</p>
     *
     * @param
     * @param tempo - Representa a hora do evento impressa em segundos.
     * @param evento - Representa a descri��o do evento.
     */
    private void updateLog(String tempo, String evento) {
        int x = log.length;

        Object[][] log_tmp = new Object[x + 1][2];

        for (int i = 0; i < log.length; i++) {
            log_tmp[i] = log[i];
        }

        log_tmp[x][0] = tempo;
        log_tmp[x][1] = evento;

        log = log_tmp;

        update_Lista_Log();
    }

    /**
     * <p>
     * Devolve o tempo em formato H:m:s .</p>
     *
     * @return time - O tempo formatado.
     */
    public String getTime() {
        SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("H:m:s");
        return DATE_FORMAT.format(new Date());
    }

    public String printLog() {
        String s = "";

        for (int i = 0; i < log.length; i++) {
            s += log[i][0] + " * " + log[i][1];
        }

        return s;
    }
}
